#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <assert.h>
#include "hrd_displayer.h"
#include "hrd_boundary_displayer.h"
#include "hrd_table.h"

#define MAX_COLOR_KIND 26

struct rgb_s {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t __for_pack__;
};

const struct rgb_s base_color_table[MAX_COLOR_KIND] = {
    {255, 0, 0},
    {255, 255, 255},
    {0, 0, 0},
    {254, 164, 127},
    {37, 204, 247},
    {234, 181, 67},
    {85, 230, 193},
    {202, 211, 200},
    {249, 127, 81},
    {27, 156, 252},
    {248, 239, 186},
    {88, 177, 159},
    {44, 58, 71},
    {179, 55, 113},
    {59, 59, 152},
    {253, 114, 114},
    {154, 236, 219},
    {214, 162, 232},
    {109, 33, 79},
    {24, 44, 97},
    {252, 66, 123},
    {189, 197, 129},
    {130, 88, 159},
    {0, 255, 0},
    {0, 0, 255},
    {255, 250, 101}
};


static void KnuthShuffle(void *array, size_t element_size, uint64_t array_length)
{
    int64_t i, j;
    uint8_t *p_array = array;
    uint8_t *buffer = malloc(element_size);

    assert(buffer != NULL);
    for (i = array_length - 1; i >= 0; i--) {
        j = rand() % (i + 1);
        memcpy(buffer, p_array + (element_size * i),                        element_size);
        memcpy(p_array + (element_size * i), p_array + (element_size * j),  element_size);
        memcpy(p_array + (element_size * j), buffer,                        element_size);
    }

    free(buffer);
}


void filterColorTable(struct rgb_s *color_table, uint8_t object_number)
{
    // TODO : To filter out those colors that close in displaying.
}


void generateColorTable(hrd_problem_t *hrd_problem, struct rgb_s *color_table)
{
    signed int seed = (signed int)hrd_problem;

    srand(seed);
    memcpy(color_table, base_color_table, sizeof(struct rgb_s) * MAX_COLOR_KIND);
    KnuthShuffle(color_table + 1, sizeof(struct rgb_s), MAX_COLOR_KIND - 1);
    filterColorTable(color_table, hrd_problem->object_number);
}


void HrdColorDisplayer_ShowColorMapping(hrd_problem_t *hrd_problem)
{
    assert(hrd_problem->object_number <= MAX_COLOR_KIND);

    uint8_t i;
    struct rgb_s color_table[MAX_COLOR_KIND];

    generateColorTable(hrd_problem, color_table);
    for (i = 0; i < hrd_problem->object_number; i++) {
        printf("index %2u : \033[38;2;%u;%u;%um■■■■\033[0m %u,%u,%u\n", i + 1,
            color_table[i].r, color_table[i].g, color_table[i].b,
            color_table[i].r, color_table[i].g, color_table[i].b
        );
    }
}


void HrdColorDisplayer_DisplayMapInColorizeBlock(int8_t table[8][8], hrd_problem_t *hrd_problem, bool show_grid)
{
    int i, j;
    struct rgb_s object_color;
    uint8_t color_index;
    struct rgb_s color_table[MAX_COLOR_KIND];

    generateColorTable(hrd_problem, color_table);

    HrdBoundaryDisplayer_DisplayerTopBoundary(table[0]);

    for (i = 1; i < 7; i++) {
        HrdBoundaryDisplayer_DisplayerLeftBoundary(table[i][0]);
        for (j = 1; j < 7; j++) {
            if (table[i][j] < 1 || table[i][j] > 26) {
                printf(" %s", show_grid ? "□" : " ");
                continue;
            }

            color_index = table[i][j] - 1;
            object_color = color_table[color_index];
            printf("\033[38;2;%u;%u;%um ■\033[0m",
                object_color.r, object_color.g, object_color.b
            );
        }
        HrdBoundaryDisplayer_DisplayerRightBoundary(table[i][7]);
    }

    HrdBoundaryDisplayer_DisplayerBottomBoundary(table[7]);
}